前一篇已經透過一個很簡單的generic function例子學到一點點generic最基本語法:
function getSomething<T>(arg: T): T {
return arg
}
雖然目前為止只學到使用一個型別變數(type variable)的generic function,但是一般情況下,一個型別變數其實已經很夠用,因為型別變數也可以搭配其他好用的參考型別,譬如之前學過的 Union
。
不過事實上,generic語法也允許有一個以上的型別變數:
function getMapElement<K, V extends keyof K>(map: Map<K, V>, key: K): V | undefined {
return map.get(key);
}
這次範例為了展示多型別變數的generic函式而稍微複雜一點,其中的 extends keyof K
這段程式碼會在明天的文章說明,這邊可以暫時不管。
範例使用JavaScript ES6以後新增的Map
,Map
是一種類似於Object卻能儲存任意型別key-value pair的特殊容器。
雖然 Map
可以指定任意型別的 key 或 value,不過某些時候也許會想要指定 Map
的key或value只能是一種或幾種型別,因此範例刻意指定Map的key型別為K、value為V。
以上會特地說明generic可以有多個型別變數,是因為generic語法其實也可以用來建立generic interface和generic class,比起函式,generic interface和generic class可能更容易碰到需要多個型別變數的情境。
先來看一下單一型別變數和多型別變數的generic interface語法,大致的語法如下:
// single type parameter
interface InterfaceName1<T> {
...
}
// multiple type parameter
interface InterfaceName2<U, V> {
...
}
Generic interface的語法和平時在使用的interface差不多,只是多了 <>
語法,以及要把型別替換成型別變數,如下範例:
interface Pair<K, V> {
// properties
key: K;
value: V;
// methods
getValue(): V;
}
const pair: Pair<string, number> = {
key: 'KEY',
value: 12345,
getValue(){
return this.value;
}
}
console.log(pair.getValue());
單一型別變數和多型別變數的Generic class則是如下:
class ClassName1<T> {
...
}
class ClassName2<U, V> {
...
}
和generic interface的狀況一樣,generic class語法也是和平時在寫class差不多,這次試著用generic class模擬非常陽春版的 Map
:
class MyMap<K, V> {
private pairs: Record<string, V>;
private static mapSize = 0;
constructor() {
this.pairs = {};
}
set(key: K, value: V) {
this.pairs[JSON.stringify(key)!] = value;
++MyMap.mapSize;
}
get(key: K) {
return this.pairs[JSON.stringify(key)];
}
get size() {
return MyMap.mapSize;
}
}
const myMap = new MyMap<any, any>();
myMap.set(1, "Hello");
myMap.set(true, "Hello World");
console.log(myMap.size); // 2
console.log(myMap.get(true)); // "Hello World"
關於類別有一點要特別注意,generic的型別變數無法用在靜態成員(static member),也就是無法用在靜態屬性(static properties)和靜態方法(static method)。
generic的型別變數只能用在實例(instance)屬性和方法,所以上面範例的靜態屬性 size
暗地裡其實會被推導成 number
型別而非generic型別變數。
最後還需留意TypeScript沒有所謂的generic enums和generic namespaces,而其他型別的generic用法可以參考官方文件或是其他網路好文。
參考資料
Generic @TypeScript Handbook
TypeScript